#include<iostream>
#include<curl/curl.h>
#include<string>
#include<vector>
#include<map>
#include<regex>
#include<filesystem>
#include<mutex>

using namespace std;

const static string URL = "http://127.0.0.1:8088";
const static string _HEADERS_COOKIES_KEY = "cookies";
const static string PATH = "./images";
vector<map<string, string>> VT_IMAGE = vector<map<string, string>>();
const static int THREAD_NUMS = 5;
mutex IMAGE_DOWN_LOCK;

size_t writeCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
	size_t totalSize = size * nmemb;
	output->append((char*)contents, totalSize);
	return totalSize;
}

string _Request_Get(const string& url, map<string, string> headers)
{
	CURL* curl;
	CURLcode statusCode;
	string htmlData;

	curl_global_init(CURL_GLOBAL_DEFAULT);
	curl = curl_easy_init();
	if (curl) {
		// 设置要抓取的网页地址
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
		if (headers.find(_HEADERS_COOKIES_KEY) != headers.end())
		{
			curl_easy_setopt(curl, CURLOPT_COOKIE, headers[_HEADERS_COOKIES_KEY].c_str());
		}
		curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
		// 设置回调函数，用于处理获取到的响应数据
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &htmlData);
		// 执行 HTTP 请求
		auto res = curl_easy_perform(curl);
		if (res != CURLE_OK) {
			std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
		}
		// 清理 CURL 实例
		curl_easy_cleanup(curl);
		return htmlData;
	}
	else {
		std::cerr << "curl_easy_init() failed." << std::endl;
		return "";
	}
	// 清理 libcurl
	curl_global_cleanup();
}

bool _In_Vector(const string& value, vector<string>& vt)
{
	return (find(vt.begin(), vt.end(), value) != vt.end());
}

void _Init(const string& html)
{
	//./img/movie10.png
	regex reg("/img/(\\w+)\\.(jpg|png)");
	sregex_iterator it(html.begin(), html.end(), reg);
	auto end_it = sregex_iterator();
	vector<string> v0;
	map<string, string> ResultMap;
	while(it != end_it)
	{
		if (_In_Vector(it->str(), v0))
		{
			++it;
			continue;
		}
		v0.push_back(it->str());
		ResultMap["url"] = it->str();
		ResultMap["image_name"] = (*it)[1].str() + (*it)[2].str();
		VT_IMAGE.push_back(ResultMap);
		//cout << it->str() << endl;
		++it;
	}
}

size_t write_callback(void* ptr, size_t size, size_t nmemb, void* stream) {
	return fwrite(ptr, size, nmemb, (FILE*)stream);
}

int download_file(const char* url, const char* output_filename) {
	CURL* curl = curl_easy_init();
	FILE* fp = fopen(output_filename, "wb");  // 打开一个文件用于保存下载的数据

	if (curl && fp) {
		// 设置下载选项
		curl_easy_setopt(curl, CURLOPT_URL, url);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);

		// 执行下载请求
		CURLcode res = curl_easy_perform(curl);
		if (res != CURLE_OK) {
			fprintf(stderr, "下载失败: %s\n", curl_easy_strerror(res));
			fclose(fp);
			curl_easy_cleanup(curl);
			return -1;
		}

		fclose(fp);
	}
	else {
		fprintf(stderr, "初始化失败\n");
		if (fp) {
			fclose(fp);
		}
		if (curl) {
			curl_easy_cleanup(curl);
		}
		return -1;
	}

	curl_easy_cleanup(curl);
	return 0;
}

void _Worker_Down_Image()
{
	string _url, _name;
	map<string, string> resultMap;
	while (true)
	{
		IMAGE_DOWN_LOCK.lock();
		if (VT_IMAGE.empty())
		{
			IMAGE_DOWN_LOCK.unlock();
			return;
		}
		resultMap = VT_IMAGE.back();
		VT_IMAGE.pop_back();
		IMAGE_DOWN_LOCK.unlock();
		_url = URL + "/" + resultMap["url"];
		_name = PATH + "/" + resultMap["image_name"];
		const char* file_url = _url.c_str();
		const char* file_name = _name.c_str();
		download_file(file_url, file_name);
		cout << "线程号：" << this_thread::get_id() << "完成图片下载：" << resultMap["image_name"] << endl;
	}
}

int main()
{
	//SetConsoleOutputCP(65001);
	if (!filesystem::exists(PATH))
	{
		filesystem::create_directory(PATH);
	}
	map<string, string> headers;
	string html;
	headers[_HEADERS_COOKIES_KEY] = "PHPSESSID=pc2fdl1l8qf3gm2f1edripaapu";
	html = _Request_Get(URL, headers);
	_Init(html);
	thread threads[THREAD_NUMS];
	for (int index = 0; i < THREAD_NUMS; ++index)
	{
		threads[index] = thread(_Worker_Down_Image);
	}
	for (int index = 0; index < THREAD_NUMS; ++index)
	{
		threads[index].join();
	}
}